package it.gtd.swimpool.model.business;

import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import it.gtd.swimpool.model.entity.User;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import static com.google.common.base.Preconditions.*;

@Stateless
@LocalBean
@Beta
public class UserManager {
    private static final String EMAIL_REGISTER_SUBJECT = "Welcome to SWIMv2!";
    private static final String EMAIL_REGISTER_BODY = "Your account has been created and is waiting for you to activate it.\n" +
            "To activate it, please, click on the following link: %s%s";
    private static final String ACTIVATION_LINK_ROUTE = "http://localhost:8080/swimpool/controller/activate/";

    @PersistenceContext
    private EntityManager entityManager;

    @EJB
    private CommunicationManager communicationManager;

    @EJB
    private ActivationManager activationManager;


    public UserManager() {
    }

    @VisibleForTesting
    UserManager(EntityManager entityManager, CommunicationManager communicationManager, ActivationManager activationManager) {
        this();

        checkNotNull(entityManager, "entityManager can't be null");
        checkNotNull(communicationManager, "communicationManager can't be null");
        checkNotNull(activationManager, "activationManager can't be null");

        this.entityManager = entityManager;
        this.communicationManager = communicationManager;
        this.activationManager = activationManager;
    }

    public User register(String email, String password) {
        checkNotNull(email, "email can't be null");
        checkNotNull(password, "password can't be null");
        checkArgument(!email.isEmpty(), "email string is empty");
        checkArgument(!password.isEmpty(), "password is empty");

        TypedQuery<User> selectUserByEmail = entityManager.createNamedQuery("User.byEmail", User.class);
        selectUserByEmail.setParameter("email", email);
        try {
            User user = selectUserByEmail.getSingleResult();
            checkArgument(false, "user with this email already exists");
        } catch (NoResultException _) {
        }

        User user = new User(email);
        user.setPassword(password);
        entityManager.persist(user);

        checkState(entityManager.contains(user), "User " + user.getEmail() + " isn't managed object");

        String activationHash = activationManager.createActivationHash(user);
        communicationManager.sendEmail(user, EMAIL_REGISTER_SUBJECT, String.format(EMAIL_REGISTER_BODY, ACTIVATION_LINK_ROUTE, activationHash));

        return user;
    }

    public User activate(String hash) {
        checkNotNull(hash, "hash can't be null");

        User user = activationManager.get(hash);

        checkNotNull(user);
        checkArgument(!user.isActive(), "User is alredy activated");

        user.activate();

        if (!entityManager.contains(user)) {
            entityManager.merge(user);
        }

        checkState(user.isActive(), "User is not active before activation!");

        activationManager.removeActivationHash(hash);

        return user;
    }
}
